﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace NetAssist.TruthTable
{
  /// <summary>
  /// A class that provides methods to evaluate Truth Tables
  /// </summary>
  public static class TruthTableEvaluator
  {
    /// <summary>
    /// Evaluates a truth table with two inputs.
    /// </summary>
    /// <param name="truthTable">A System.Collections.Generic.IEnumerable&lt;bool&gt; with 4 elements representing two inputs truetTable.</param>
    /// <param name="inputA">First System.Collections.Generic.IEnumerable&lt;bool&gt; to use as the input of the truth table.</param>
    /// <param name="inputB">Second System.Collections.Generic.IEnumerable&lt;bool&gt; to use as the input of the truth table.</param>
    /// <returns>
    /// A System.Collections.Generic.IEnumerable&lt;bool&gt; with values that match the truth table evaluation for the elements from inputA and inputB.
    /// If inputA and inputB are not of the same length the returned sequence will have as many elements as the shorter of the two inputs.
    /// </returns>
    /// <exception cref="System.ArgumentNullException">
    /// truthTable or inputA or inputB is null.
    /// </exception>
    /// <exception cref="System.ArgumentException">
    /// truthTable has too few or too many elements, the truthTable must have exactly 4 elements.
    /// </exception>
    public static IEnumerable<bool> Evaluate(this IEnumerable<bool> truthTable, IEnumerable<bool> inputA, IEnumerable<bool> inputB)
    {
      Helper.InvalidateNullParam(truthTable, "truthTable");
      Helper.InvalidateNullParam(inputA, "inputA");
      Helper.InvalidateNullParam(inputB, "inputB");

      if (truthTable.Count() != 4)
        throw new ArgumentException("Parameter 'truthTable' must contain exactly 4 elements.", "truthTable");

      Table tt = new Table(truthTable);
      tt.Simplify();
      Func<bool, bool, bool> ttFunc = (Func<bool, bool, bool>)tt.GetTruthTableDelegate();

      using (IEnumerator<bool> inpA = inputA.GetEnumerator())
      using (IEnumerator<bool> inpB = inputB.GetEnumerator())
        while (inpA.MoveNext() && inpB.MoveNext())
          yield return ttFunc(inpA.Current, inpB.Current);
    }

    /// <summary>
    /// Evaluates a truth table with three inputs.
    /// </summary>
    /// <param name="truthTable">A System.Collections.Generic.IEnumerable&lt;bool&gt; with 8 elements representing three inputs truetTable.</param>
    /// <param name="inputA">First System.Collections.Generic.IEnumerable&lt;bool&gt; to use as the input of the truth table.</param>
    /// <param name="inputB">Second System.Collections.Generic.IEnumerable&lt;bool&gt; to use as the input of the truth table.</param>
    /// <param name="inputC">Third System.Collections.Generic.IEnumerable&lt;bool&gt; to use as the input of the truth table.</param>
    /// <returns>
    /// A System.Collections.Generic.IEnumerable&lt;bool&gt; with values that match the truth table evaluation for the elements from inputA, inputB and inputC.
    /// If inputA, inputB and inputC are not of the same length the returned sequence will have as many elements as the shorter of the three inputs.
    /// </returns>
    /// <exception cref="System.ArgumentNullException">
    /// truthTable or inputA or inputB or inputC is null.        
    /// </exception>
    /// <exception cref="System.ArgumentException">
    /// truthTable has too few or too many elements, the truthTable must have exactly 8 elements.
    /// </exception>
    public static IEnumerable<bool> Evaluate(this IEnumerable<bool> truthTable, IEnumerable<bool> inputA, IEnumerable<bool> inputB, IEnumerable<bool> inputC)
    {
      Helper.InvalidateNullParam(inputA, "inputA");
      Helper.InvalidateNullParam(inputB, "inputB");
      Helper.InvalidateNullParam(inputC, "inputC");

      if (truthTable.Count() != 8)
        throw new ArgumentException("Parameter 'truthTable' must contain exactly 8 elements.", "truthTable");

      Table tt = new Table(truthTable);
      tt.Simplify();
      Func<bool, bool, bool, bool> ttFunc = (Func<bool, bool, bool, bool>)tt.GetTruthTableDelegate();

      using (IEnumerator<bool> inpA = inputA.GetEnumerator())
      using (IEnumerator<bool> inpB = inputB.GetEnumerator())
      using (IEnumerator<bool> inpC = inputC.GetEnumerator())
        while (inpA.MoveNext() && inpB.MoveNext() && inpC.MoveNext())
          yield return ttFunc(inpA.Current, inpB.Current, inpC.Current);
    }

    /// <summary>
    /// Evaluates a truth table with four inputs.
    /// </summary>
    /// <param name="truthTable">A System.Collections.Generic.IEnumerable&lt;bool&gt; with 16 elements representing four inputs truetTable.</param>
    /// <param name="inputA">First System.Collections.Generic.IEnumerable&lt;bool&gt; to use as the input of the truth table.</param>
    /// <param name="inputB">Second System.Collections.Generic.IEnumerable&lt;bool&gt; to use as the input of the truth table.</param>
    /// <param name="inputC">Third System.Collections.Generic.IEnumerable&lt;bool&gt; to use as the input of the truth table.</param>
    /// <param name="inputD">Fourth System.Collections.Generic.IEnumerable&lt;bool&gt; to use as the input of the truth table.</param>
    /// <returns>
    /// A System.Collections.Generic.IEnumerable&lt;bool&gt; with values that match the truth table evaluation for the elements from inputA through inputD.
    /// If all inputs are not of the same length the returned sequence will have as many elements as the shorter of the four inputs.
    /// </returns>
    /// <exception cref="System.ArgumentNullException">
    /// truthTable or inputA or inputB or inputC or inputD is null.    
    /// </exception>
    /// <exception cref="System.ArgumentException">
    /// truthTable has too few or too many elements, the truthTable must have exactly 16 elements.
    /// </exception>
    public static IEnumerable<bool> Evaluate(this IEnumerable<bool> truthTable, IEnumerable<bool> inputA, IEnumerable<bool> inputB, IEnumerable<bool> inputC, IEnumerable<bool> inputD)
    {
      Helper.InvalidateNullParam(inputA, "inputA");
      Helper.InvalidateNullParam(inputB, "inputB");
      Helper.InvalidateNullParam(inputC, "inputC");
      Helper.InvalidateNullParam(inputD, "inputD");

      if (truthTable.Count() != 16)
        throw new ArgumentException("Parameter 'truthTable' must contain exactly 16 elements.", "truthTable");

      Table tt = new Table(truthTable);
      tt.Simplify();
      Func<bool, bool, bool, bool, bool> ttFunc = (Func<bool, bool, bool, bool, bool>)tt.GetTruthTableDelegate();

      using (IEnumerator<bool> inpA = inputA.GetEnumerator())
      using (IEnumerator<bool> inpB = inputB.GetEnumerator())
      using (IEnumerator<bool> inpC = inputC.GetEnumerator())
      using (IEnumerator<bool> inpD = inputD.GetEnumerator())
        while (inpA.MoveNext() && inpB.MoveNext() && inpC.MoveNext() && inpD.MoveNext())
          yield return ttFunc(inpA.Current, inpB.Current, inpC.Current, inpD.Current);
    }

    /// <summary>
    /// Evaluates a truth table with five inputs.
    /// </summary>
    /// <param name="truthTable">A System.Collections.Generic.IEnumerable&lt;bool&gt; with 32 elements representing five inputs truetTable.</param>
    /// <param name="inputA">First System.Collections.Generic.IEnumerable&lt;bool&gt; to use as the input of the truth table.</param>
    /// <param name="inputB">Second System.Collections.Generic.IEnumerable&lt;bool&gt; to use as the input of the truth table.</param>
    /// <param name="inputC">Third System.Collections.Generic.IEnumerable&lt;bool&gt; to use as the input of the truth table.</param>
    /// <param name="inputD">Fourth System.Collections.Generic.IEnumerable&lt;bool&gt; to use as the input of the truth table.</param>
    /// <param name="inputE">Fifth System.Collections.Generic.IEnumerable&lt;bool&gt; to use as the input of the truth table.</param>
    /// <returns>
    /// A System.Collections.Generic.IEnumerable&lt;bool&gt; with values that match the truth table evaluation for the elements from inputA through inputE.
    /// If all inputs are not of the same length the returned sequence will have as many elements as the shorter of the four inputs.
    /// </returns>
    /// <exception cref="System.ArgumentNullException">
    /// truthTable or inputA or inputB or inputC or inputD or inputE is null.
    /// </exception>
    /// <exception cref="System.ArgumentException">
    /// truthTable has too few or too many elements, the truthTable must have exactly 32 elements.
    /// </exception>
    public static IEnumerable<bool> Evaluate(this IEnumerable<bool> truthTable, IEnumerable<bool> inputA, IEnumerable<bool> inputB, IEnumerable<bool> inputC, IEnumerable<bool> inputD, IEnumerable<bool> inputE)
    {
      Helper.InvalidateNullParam(inputA, "inputA");
      Helper.InvalidateNullParam(inputB, "inputB");
      Helper.InvalidateNullParam(inputC, "inputC");
      Helper.InvalidateNullParam(inputD, "inputD");
      Helper.InvalidateNullParam(inputE, "inputE");

      if (truthTable.Count() != 32)
        throw new ArgumentException("Parameter 'truthTable' must contain exactly 32 elements.", "truthTable");

      Table tt = new Table(truthTable);
      tt.Simplify();
      Func<bool, bool, bool, bool, bool, bool> ttFunc = (Func<bool, bool, bool, bool, bool, bool>)tt.GetTruthTableDelegate();

      using (IEnumerator<bool> inpA = inputA.GetEnumerator())
      using (IEnumerator<bool> inpB = inputB.GetEnumerator())
      using (IEnumerator<bool> inpC = inputC.GetEnumerator())
      using (IEnumerator<bool> inpD = inputD.GetEnumerator())
      using (IEnumerator<bool> inpE = inputE.GetEnumerator())
        while (inpA.MoveNext() && inpB.MoveNext() && inpC.MoveNext() && inpD.MoveNext() && inpE.MoveNext())
          yield return ttFunc(inpA.Current, inpB.Current, inpC.Current, inpD.Current, inpE.Current);
    }

    /// <summary>
    /// Evaluates a truth table with six inputs.
    /// </summary>
    /// <param name="truthTable">A System.Collections.Generic.IEnumerable&lt;bool&gt; with 64 elements representing six inputs truetTable.</param>
    /// <param name="inputA">First System.Collections.Generic.IEnumerable&lt;bool&gt; to use as the input of the truth table.</param>
    /// <param name="inputB">Second System.Collections.Generic.IEnumerable&lt;bool&gt; to use as the input of the truth table.</param>
    /// <param name="inputC">Third System.Collections.Generic.IEnumerable&lt;bool&gt; to use as the input of the truth table.</param>
    /// <param name="inputD">Fourth System.Collections.Generic.IEnumerable&lt;bool&gt; to use as the input of the truth table.</param>
    /// <param name="inputE">Fifth System.Collections.Generic.IEnumerable&lt;bool&gt; to use as the input of the truth table.</param>
    /// <param name="inputF">Sixth System.Collections.Generic.IEnumerable&lt;bool&gt; to use as the input of the truth table.</param>
    /// <returns>
    /// A System.Collections.Generic.IEnumerable&lt;bool&gt; with values that match the truth table evaluation for the elements from inputA through inputF.
    /// If all inputs are not of the same length the returned sequence will have as many elements as the shorter of the four inputs.
    /// </returns>
    /// <exception cref="System.ArgumentNullException">
    /// truthTable or inputA or inputB or inputC or inputD or inputE  or inputF is null.
    /// </exception>
    /// <exception cref="System.ArgumentException">
    /// truthTable has too few or too many elements, the truthTable must have exactly 64 elements.
    /// </exception>
    public static IEnumerable<bool> Evaluate(this IEnumerable<bool> truthTable, IEnumerable<bool> inputA, IEnumerable<bool> inputB, IEnumerable<bool> inputC, IEnumerable<bool> inputD, IEnumerable<bool> inputE, IEnumerable<bool> inputF)
    {
      Helper.InvalidateNullParam(inputA, "inputA");
      Helper.InvalidateNullParam(inputB, "inputB");
      Helper.InvalidateNullParam(inputC, "inputC");
      Helper.InvalidateNullParam(inputD, "inputD");
      Helper.InvalidateNullParam(inputE, "inputE");
      Helper.InvalidateNullParam(inputF, "inputF");

      if (truthTable.Count() != 64)
        throw new ArgumentException("Parameter 'truthTable' must contain exactly 64 elements.", "truthTable");

      Table tt = new Table(truthTable);
      tt.Simplify();
      Func<bool, bool, bool, bool, bool, bool, bool> ttFunc = (Func<bool, bool, bool, bool, bool, bool, bool>)tt.GetTruthTableDelegate();

      using (IEnumerator<bool> inpA = inputA.GetEnumerator())
      using (IEnumerator<bool> inpB = inputB.GetEnumerator())
      using (IEnumerator<bool> inpC = inputC.GetEnumerator())
      using (IEnumerator<bool> inpD = inputD.GetEnumerator())
      using (IEnumerator<bool> inpE = inputE.GetEnumerator())
      using (IEnumerator<bool> inpF = inputF.GetEnumerator())
        while (inpA.MoveNext() && inpB.MoveNext() && inpC.MoveNext() && inpD.MoveNext() && inpE.MoveNext() && inpF.MoveNext())
          yield return ttFunc(inpA.Current, inpB.Current, inpC.Current, inpD.Current, inpE.Current, inpF.Current);
    }

  }
}
